package com.fanshuai.micrometers;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.config.MeterFilterReply;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Collector;

import java.lang.management.ManagementFactory;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class TestMeter {

    public MeterRegistry meterRegistry;
    public PrometheusMeterRegistry prometheusMeterRegistry;

    public TestMeter() {
        //MeterRegistry  meter容器，可以注册meter 输出meter
        SimpleMeterRegistry simpleMeterRegistry = new SimpleMeterRegistry();

        //将meter导出到prometheus
        PrometheusMeterRegistry prometheusMeterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);

        //组合MeterRegistry, 将多个MeterRegistry结合使用，可以输出到多个监控系统
        CompositeMeterRegistry compositeMeterRegistry = new CompositeMeterRegistry();
        compositeMeterRegistry.add(simpleMeterRegistry);
        compositeMeterRegistry.add(prometheusMeterRegistry);

        /**
         * 名称转换  不同的监控系统有不同的命名约定
         */
        //compositeMeterRegistry.config().namingConvention();

        /**
         * Common tags可以被定义在registry级别，并且会被添加到每个监控系统的报告中
         * 预定义的Tags有host=instance, region=stack
         */
        //compositeMeterRegistry.config().commonTags("host", "instance", "region", "stack");

        /**
         * 添加meterfilter  使用IgnoreTags等静态函数生成MeterFilter
         */
        compositeMeterRegistry.config()
                .meterFilter(MeterFilter.ignoreTags("testaaa"))
                .meterFilter(MeterFilter.denyNameStartsWith("testbb"));

        /**
         * 自定义MeterFilter accept/deny 或transform
         */
        MeterFilter meterFilter = new MeterFilter() {
            @Override
            public MeterFilterReply accept(Meter.Id id) {
                if (id.getName().contains("test")) {
                    return MeterFilterReply.DENY;
                }
                return MeterFilterReply.ACCEPT;
            }
        };
        MeterFilter mapFilter = new MeterFilter() {
            @Override
            public Meter.Id map(Meter.Id id) {
                return id.withName("com.fanshuai." + id.getName());
            }
        };

        compositeMeterRegistry.config().meterFilter(meterFilter).meterFilter(mapFilter);

        this.prometheusMeterRegistry = prometheusMeterRegistry;
        this.meterRegistry = compositeMeterRegistry;
    }

    public void testCounter() {
        Counter counter = meterRegistry.counter("counter");
        for (int i = 0; i < 10; i++) {
            counter.increment();
        }
        System.out.println(counter.count());
        System.out.println("measurement=" + counter.measure());

        Counter counter1 = Counter.builder("counter1")
                .baseUnit("count").description("counter1 ")
                .tags("region", "test")
                .register(meterRegistry);

        for (int i = 0; i < 10; i++) {
            counter1.increment();
        }
        System.out.println(counter1.count());
        System.out.println("counter1 measurement=" + counter1.measure());

        /**
         * FunctionCounter
         */
        Random random = new Random(System.currentTimeMillis());
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String s) throws Exception {
                        return random.nextInt() % 1000;
                    }
                });

        List<Tag> tags = new ArrayList<>();
        FunctionCounter functionCounter = meterRegistry.more()
                .counter("evictions", tags, cache, c -> c.stats().hitCount());

        for (int i = 0; i < 10; i++) {
            System.out.println("function counter=" + functionCounter.count());
        }
    }

    public class Record {
        public int value;
    }

    public void testGauge() {

        meterRegistry.gauge("thread-count", ManagementFactory.getThreadMXBean(), bean -> {
            return bean.getThreadCount();
        });
        Gauge gauge = meterRegistry.get("com.fanshuai.thread-count").gauge();

        Record record = new Record();
        List<Tag> tags = new ArrayList<>();
        meterRegistry.gauge("record", tags, record, r -> {
            return r.value;
        });
        Gauge recordGauge = meterRegistry.get("com.fanshuai.record").gauge();

        Random random = new Random();
        random.setSeed(System.currentTimeMillis());
        for (int i = 0; i < 10; i++) {
            record.value = random.nextInt(100);

            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("thread count=" + gauge.value());
            System.out.println("record=" + recordGauge.value());
        }

        /**
         * Gauge记录瞬时值，TimeGauge记录瞬时时间
         */
        TimeGauge timeGauge = meterRegistry.more().timeGauge("timegauge", new ArrayList<>(), record, TimeUnit.SECONDS, r -> r.value);
        System.out.println(timeGauge.measure());

        System.out.println("time value=" + timeGauge.value(TimeUnit.SECONDS));
    }



    public void testTimer() {
        Timer timer = meterRegistry.timer("timer");

        Random random = new Random(System.currentTimeMillis());

        for (int i = 0; i < 10; i++) {
            int value = random.nextInt(1000);
            timer.record(value, TimeUnit.MILLISECONDS);

            System.out.println("value=" + value);
        }

        System.out.println("count=" + timer.count());
        System.out.println("max=" + timer.max(TimeUnit.MILLISECONDS));
        System.out.println("mean=" + timer.mean(TimeUnit.MILLISECONDS));
        System.out.println("total time=" + timer.totalTime(TimeUnit.MILLISECONDS));
        System.out.println("measurement=" + timer.measure());

        Timer timer1 = Timer.builder("timer1")
                .description("timer1")
                .tags()
                .register(meterRegistry);
        System.out.println(timer1.measure());

        Timer timer2 = Timer.builder("timer2")
                .publishPercentiles(0.5, 0.75, 0.9, 0.95, 0.98, 0.99)  //输出p50 p75 p90 p95 p98 p99
                .publishPercentileHistogram(false)  //是否输出直方图bucket 默认不输出
                .register(meterRegistry);

        for (int i = 0; i < 10; i++) {
            int value = random.nextInt(1000);
            timer2.record(value, TimeUnit.MILLISECONDS);

            System.out.println("value=" + value);
        }

        System.out.println("count=" + timer2.count());
        System.out.println("max=" + timer2.max(TimeUnit.MILLISECONDS));
        System.out.println("mean=" + timer2.mean(TimeUnit.MILLISECONDS));
        System.out.println("total time=" + timer2.totalTime(TimeUnit.MILLISECONDS));
        System.out.println("measurement=" + timer2.measure());
    }

    /**
     * Summary 用于直方图统计
     */
    public void testSummary() {
        DistributionSummary summary = meterRegistry.summary("summary");
        Random random = new Random(System.currentTimeMillis());

        for (int i = 0; i < 10; i++) {
            summary.record(random.nextInt(100));
        }

        System.out.println("count=" + summary.count());
        System.out.println("max=" + summary.max());
        System.out.println("mean=" + summary.mean());
        System.out.println("total amount=" + summary.totalAmount());
        System.out.println("measurement=" + summary.measure());

        DistributionSummary summary1 = DistributionSummary.builder("summary1")
                .publishPercentiles(0.5, 0.75, 0.9, 0.95, 0.98, 0.99)   //输出p50 p75 p90 p95 p98 p99
                .publishPercentileHistogram(false)  //是否输出直方图bucket 默认不输出
                .register(meterRegistry);

        for (int i = 0; i < 10; i++) {
            summary1.record(random.nextInt(100));
        }

        System.out.println("count=" + summary1.count());
        System.out.println("max=" + summary1.max());
        System.out.println("mean=" + summary1.mean());
        System.out.println("total amount=" + summary1.totalAmount());
        System.out.println("measurement=" + summary1.measure());
    }

    public void testfunctionTimer() {
        /**
         * Timer记录次数与时间，LongTaskTimer记录任务数和任务时长
         */
        LongTaskTimer longTaskTimer = meterRegistry.more().longTaskTimer("long-task-timer");

        longTaskTimer.record(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(200);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(longTaskTimer.measure());
    }

    public static void main(String[] args) {
        TestMeter testMeter = new TestMeter();
        testMeter.testCounter();
        testMeter.testGauge();

        testMeter.testTimer();
        testMeter.testfunctionTimer();

        testMeter.testSummary();

        //PrometheusMeterRegistry output
        String output = testMeter.prometheusMeterRegistry.scrape();
        System.out.println(output);

        Enumeration<Collector.MetricFamilySamples> samplesEnumeration = testMeter.prometheusMeterRegistry.getPrometheusRegistry().metricFamilySamples();

        while (samplesEnumeration.hasMoreElements()) {
            Collector.MetricFamilySamples samples = samplesEnumeration.nextElement();
            System.out.println(samples.samples);
        }
    }
}
